﻿/************************************************************************
 * SMLib - State Model Library
 * Copyright (C) 2012-2013 Ali Khalili (khalili_DOT_ir_@SIGN_gmail_DOT_com)
 * **********************************************************************
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License (LGPL) published 
 * by the Free Software Foundation; either version 2.1 of the License, or (at your
 * option) any later version. This program is distributed without any
 * warranty. See the GNU Lesser General Public License for more details. 
 * ***********************************************************************
 * File         :     SMLib\Automata\Base\Word.cs
 * Description  :     A list of symbols
 * Change log   :     -22 Feb 2013: Polishing the file
 *                    -Jan 2013:  Initial version
 * To Do        :	  Make word inherited from list or no?
 ************************************************************************/
using System;
using System.Collections.Generic;

namespace SMLib.Automata.Base
{
    /// <summary>
    /// A list of charachters (in Sigma) for a regular language. It might be "" which shows null.
    /// </summary>
    public class Word
    {
        List<ISymbol> steps;

        /// <summary>
        /// Copy constructor
        /// </summary>
        /// <param name="p">words to be concated as the initial value</param>
        public Word(params Word[] p)
        {
            steps = new List<ISymbol>();
            for (int i = 0; i < p.Length; i++)
            {
                for (int k = 0; k < p[i].steps.Count; k++)
                    steps.Add(p[i].steps[k]);
            }
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="symb1">The first symbol of word</param>
        /// <param name="symbs">other remaining symbols</param>
        public Word(ISymbol symb1, params ISymbol[] symbs)
        {
            steps = new List<ISymbol>();
            steps.Add(symb1);
            for (int i = 0; i < symbs.Length; i++)
            {
                steps.Add(symbs[i]);
            }
        }


        /// <summary>
        /// A word is consistent if of its symbols are from a same type
        /// </summary>
        /// <returns>true if the word is consistent, false in other case</returns>
        public bool CheckConsistency()
        {
            if (Length == 0)
                return true;
            Type t = this[0].GetType();
            foreach (ISymbol s in this.steps)
            {
                if (!t.Equals(s.GetType()))
                    return false;
            }
            return true;
        }

        /// <summary>
        /// Reverse the word (so the last symbol for example, would be the first symbol)
        /// </summary>
        public void Reverse()
        {
            for (int i = 0; i < Length / 2; i++)
            {
                ISymbol s1 = this[i];
                steps[i] = steps[Length - 1];
                steps[Length - 1] = s1;
            }
        }


        /// <summary>
        /// Finding out what is the type of symbols in the word
        /// </summary>
        /// <returns>the type of symbols in this word</returns>
        public Type TypeOfSymbols()
        {
            if (!CheckConsistency())
                throw new Exception("Inconsistent type!");

            if (Length == 0)
                return null;
            else
                return this[0].TypeOfSymbol();
        }


        /// <summary>
        /// Length/Size of the Path (number of steps)
        /// </summary>
        public int Length
        {
            get
            {
                return steps.Count;
            }
        }

        /// <summary>
        /// Check whether this word is empty (a.k.a lambda) or not
        /// </summary>
        /// <returns></returns>
        public bool IsLambda()
        {
            return steps.Count == 0;
        }

        /// <summary>
        /// Add a symbol - at the end of word
        /// </summary>
        /// <param name="st"></param>
        public Word AddSymbol(ISymbol st)
        {
            steps.Add(st);
            return this;
        }
		
		/// <summary>
		/// Adds the step in a particular index.
		/// </summary>
		/// <returns>
		/// The <see cref="Word"/>.
		/// </returns>
		/// <param name='st'>
		/// St.
		/// </param>
		/// <param name='index'>
		/// Index.
		/// </param>
		public Word AddSymbolAt(ISymbol st,int index)
        {
            steps.Insert(index,st);
            return this;
        }


		public Word RemoveSymbolAt(int index)
		{
			steps.RemoveAt(index);
			return this;
		}


        /// <summary>
        /// Add some symbols - at the end of word
        /// </summary>
        /// <param name="sts"></param>
        public Word AddSymbols(params ISymbol[] sts)
        {
            foreach (ISymbol symb in sts)
            {
                AddSymbol(symb);
            }
            return this;
        }

        /// <summary>
        /// Add a word at the end
        /// </summary>
        /// <param name="w"></param>
        public void AddWord(Word w)
        {
            for (int i = 0; i < w.Length; i++)
            {
                AddSymbol(w[i]);
            }
        }

        /// <summary>
        /// Clone the 
        /// </summary>
        /// <returns>A cloned copy of this word</returns>
        public Word Clone()
        {
            Word c = new Word(this);
            return c;
        }

		/// <summary>
		/// Clear the word symbols
		/// </summary>
		public void Clear ()
		{
			this.steps.Clear();
		}

        /// <summary>
        /// returns the symbol at the specific index
        /// <remarks>instead of this, it is preferable to use ``this[i]''</remarks>
        /// </summary>
        /// <param name="i">Index of symbol</param>
        /// <returns>The corresponding symbol</returns>
        public ISymbol GetSymByIndex(int i)
        {
            return steps[i];
        }

        /// <summary>
        /// returns the symbol at the specific index
        /// </summary>
        /// <param name="i">index</param>
        /// <returns>the symbol</returns>
        public ISymbol this[int i]
        {
            get
            {
                return steps[i];
            }
        }

        /// <summary>
        /// A path containing the prefix of the path with length 'len'
        /// </summary>
        /// <param name="len">Length of prefix</param>
        /// <returns>the prefix word</returns>
        public Word Prefix(int len)
        {
            Word p = new Word();
            for (int i = 0; i < len; i++)
                p.AddSymbol(steps[i]);
            return p;
        }


        /// <summary>
        /// A path containing the suffix of the path with length 'len'
        /// </summary>
        /// <param name="len">Length of suffix</param>
        /// <returns>the suffix word</returns>
        public Word Suffix(int len)
        {
            Word p = new Word();
            for (int i = Length - len; i < Length; i++)
                p.AddSymbol(steps[i]);
            return p;
        }

        /// <summary>
        /// Compute the length of prefix word which is common
        /// </summary>
        /// <param name="w">the other word</param>
        /// <returns>
        /// lenght of the longest common prefix of this worth with <paramref name="w"/>
        /// </returns>
        public int PrefixLength(Word w)
        {
            int min = Math.Min(w.Length, Length);
            for (int i = 0; i < min; i++)
            {
                if (!w[(i)].Equals(steps[i]))
                    return i;
            }
            return min;
        }

        /// <summary>
        /// Try to parse a word from string.
        /// <paramref name="w"/> Should has symbols 
        /// </summary>
        /// <param name="w">the word</param>
        /// <returns>the parsed word</returns>
        public static Word TryParse(string w)
        {
            if (w == null)
                throw new Exception("The input should be not null");
            Word myWord = new Word();
            string currsymb = "";
			//int i;
            for (int i = 0; i < w.Length; i++)
			//do
            {
                char c = w[i];
                if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
                {
                    currsymb += c;
					if(i==w.Length-1)
						myWord.AddSymbol(new StringSymbol(currsymb));
                }
                else if (c == '.')
                {
                    if (i == 0)
                        throw new Exception("not valid symbol! It should start with '.'");
                    if (currsymb != "")
                    {
                        myWord.AddSymbol(new StringSymbol(currsymb));
                        currsymb = "";
                    }
                    //* else: skip
                }
                else
                    throw new Exception("not valid symbol! It should be alphabet or number");
            //	i++;
			}
			//while(i<w.Length);
            return myWord;
        }

        /// <summary>
        /// Checks if this word is a prefix of the supplied word.
        /// </summary>
        /// <param name="w">the supplied word</param>
        /// <returns>true if it's a prefix of w, false in other case</returns>
        public bool IsPrefixOf(Word w)
        {
            return (w.PrefixLength(this) == Length);
        }

        /// <summary>
        /// Two path are equal myOracle NEG
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            if (!(obj is Word))
                return false;
            Word p2 = (Word)obj;
            if (p2.Length != Length)
                return false;
            for (int i = 0; i < Length; i++)
            {
				//* check the null symbols!
				if(p2.steps[i]==null && steps[i]==null)
					continue;
				else if (p2.steps[i]==null || steps[i]==null)
					return false;
				
                if (!p2.steps[i].Equals(steps[i]))
                    return false;
            }
            return true;
        }

        /// <summary>
        /// Convert a word to string (to print a word for example)
        /// </summary>
        /// <returns>the string equivalent of this word</returns>
        public override string ToString()
        {
            string s = "[";
            for (int i = 0; i < steps.Count; i++)
                s += (steps[i].ToString() + (i == steps.Count - 1 ? "]" : "."));
            if (steps.Count == 0)
                s += "]";
            return s;
        }

        /// <summary>
        /// Just to avoid waring!
        /// </summary>
        /// <returns>the hash generatedCode</returns>
        public override int GetHashCode()
        {
			return this.ToString().GetHashCode();
        }

    }

}
